package monsmon.game.parse;

import java.lang.reflect.*;
import java.util.*;
import java.util.regex.*;
import monsmon.game.*;
import monsmon.game.tmlt.*;
import monsmon.game.stile.*;
import monsmon.util.Util;

/*
 *	parses level definitions, only used by Parser
 *	will create the level and add it to the MonsGame
 */
class LvlC{

	//regex strings for ctrl segment parsing
	static final String
		__arg		= "\\s*" + Util.argument + "\\s*",
		assign_s 	= "\\s*([\\w\\d_]*)\\s*=\\s*(" + __arg + ")\\s*",
		comment_s	= "//.*$",
		alias_s  	= "\\s*alias:\\s*(.)\\s*->\\s*\"(.*)\"\\s*",
		call_s   	= "\\s*([a-zA-Z0-9_]*)\\s*\\" + 
				"(((?:\\s*" + __arg + "\\s*,)*\\s*"+__arg+"\\s*)\\)\\s*",
		arg2	 	= "\\s*(\\d+|\"[.[^\"]]*\"|[\\S[^,]]+)(?:,|)\\s*";
	//regex patterns
	static final Pattern
		assign	 = Pattern.compile(assign_s),
		arg 	 = Pattern.compile(arg2),
		alias	 = Pattern.compile(alias_s),
		call	 = Pattern.compile(call_s),
		comment  = Pattern.compile(comment_s);
	
	//Removes quotes ex: "foo" -> foo
	static final String __strip_quotes(String s){
		return s.replace("\"", "");
	}

	//runs the CTRL segment
	private static void __do_CTRL(LevelStrs lvl, MonsGame gm){
		String[] cmds = lvl.ctrl.split("\n");
		for(String cmd: cmds){
			Matcher m = comment.matcher(cmd);
			cmd = m.replaceAll("");
			m = assign.matcher(cmd);
			if(m.matches()){ 	//assign
				if(m.group(1).charAt(0) == '_'){
					System.err.println(
					"Cannot assign to a private meber: "
					+ m.group(1) + " @ " + lvl.name);
					continue;
				}
				try{
					Field f = LevelStrs.class
						.getDeclaredField(
						'_' + m.group(1));
					Class cls = f.getType();
					if(cls.equals(String.class)){
						f.set(lvl, __strip_quotes(
							m.group(2)));
					}
				}catch(Exception e){
					System.err.print(
					"Cannot assign to a member: " + 
					m.group(1) + " @ " + lvl.name);
				}
				continue;
			}
			m = call.matcher(cmd);
			if(m.matches()){ 	//Call
				Method mt;
				try{
					mt = LvlC.class.getDeclaredMethod(
						'_' + m.group(1), 
						Args.class,
						LevelStrs.class, 
						MonsGame.class);
				}catch(Exception ex){
					mt = null;
				}
				if(mt != null){
					try{
						mt.invoke(null, 
							new Args(m.group(2)), 
							lvl, gm);
					}catch(Exception ex){
						ex.printStackTrace();
						System.err.println(
							"Bad CTRL call: " + cmd
							+ " @ " + lvl.name);
					}
				}else{
					System.err.println(
						"Bad CTRL call name: "
						+ m.group(1) + " @ " + 
						lvl.name);
				}
				continue;
			}
			m = alias.matcher(cmd);
			if(m.matches()){	//Alias
				lvl.aliases.put(
					m.group(1).charAt(0), 
					lvl.tiles.get(
						__strip_quotes(m.group(2))));
				continue;
			}
			cmd = cmd.trim();
			if(!"".equals(cmd)){
				System.out.println("Unknown Command: " + cmd);
			}
		}
	}

	private static Tile __make_tile(int x, int y, 
			char type, LevelStrs lvl){
		TileT t = lvl.aliases.get(type);
		if (t == null){
			System.out.println("Bad Type: " + type);
		}
		return t.create_tile(x, y);
	}

	//
	private static void __make_map(LevelStrs lvl, MonsGame gm){
		//
		Tile[][] map = new Tile[lvl.x_size][lvl.y_size];
		int xp = 0, yp = 0;
		for(String ln : lvl.map.split("\n")){
			if(yp >= map.length){
				System.err.print("Map is too long: "+lvl.name);
				break;
			}
			//System.out.println();
			for(xp = 0; xp < map[0].length; xp++){
				Tile t = __make_tile(
					xp, yp, ln.charAt(xp), lvl);	
			//	System.out.print(t);
				map[yp][xp] = t;
			}
			yp++;
		}
		Tile[][] map2 = new Tile[lvl.y_size][lvl.x_size];
		for(int x = 0; x < lvl.x_size; x++){
			for(int y = 0; y < lvl.y_size; y++){
				map2[y][x] = map[x][y];
			}
		}
		map = map2;

		LevelMap lvlmap = new LevelMap(map, lvl.name, gm, lvl);
		gm.add_level(lvlmap);
		for(CCmd cmd: lvl.cmds){
			cmd.exec(lvl, lvlmap, gm);
		}
		gm.add_level(lvlmap);
	}

	/*
	 *	Parse the level
	 */
	static void parse(LevelStrs lvl, MonsGame gm){
		__do_CTRL(lvl, gm);
		__make_map(lvl, gm);
	}
	
	private static void print_groups(Matcher m){
		for(int i = 0; i <= m.groupCount(); i++){
			System.out.println("Group "+i+": "+m.group(i));
		}
	}
	

	///////////////////////////////////////////////////////////////////
	//			Begin CTRL Functions
	///////////////////////////////////////////////////////////////////
	//	
	//Export notes:
	//	Any function with (Matcher, LevelStrs, MonsGame) and a 
	//	'_' in front WILL be exported to CTRL segment. All
	//	such fuctions should return void.
	//
	//Param Notes:
	//	Args -- this class holds the params passed to the function
	//		1 to 6 are in a1 to a6 for convience
	//	LevelStrs -- level template being operated on
	//	MonsGame -- game being built

	//Links a tile to another
	private static void _link(Args args, LevelStrs lvl, MonsGame gm){
		lvl.cmds.add(new CCmd.Link_Cmd(
			Integer.parseInt(args.a1),
			Integer.parseInt(args.a2),
			Integer.parseInt(args.a3),
			Integer.parseInt(args.a4),
			__strip_quotes(args.a5)));
	}

	private static void _put_ent(Args args, LevelStrs lvl, MonsGame gm){
		EntT e = gm.gd.gent.get(args.a1);
		if(e == null){
			throw new IllegalArgumentException("Bad EntT name: "
					+ args.a1);
		}
		lvl.cmds.add(new CCmd.PutEntC(
				e,
				Integer.parseInt(args.a2),
				Integer.parseInt(args.a3)));
	}

	//Sets the size of the map
	private static void _mapsz(Args args, LevelStrs lvl, MonsGame gm){
		lvl.y_size = Integer.parseInt(args.a1);
		lvl.x_size = Integer.parseInt(args.a2);
		if(lvl.set_size){
			System.err.print("Warning: Setting map size twice on "
				+ lvl.name);
		}
		lvl.set_size = true;
	}

}
